<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script>
    var data = {
        test: 1,
        arr: [1, 2],
        obj: {
            p: 1
        }
    };

    //观察对象
    observe(data);


    function observeArray(arr){
        // 持有一个新对象，链到Array的原型链
        let arrProto = Object.create(Array.prototype);

        // 需要被重写的方法集合： 调用原数组对应方法，并加入render操作
        const methods = ['push'];
        methods.forEach(methodKey => {
            Object.defineProperty(arrProto, methodKey, {
                value: function(){
                   Array.prototype[methodKey]();
                   render()

                }
            })

        });

        // 把对象的原型链设置为被修改过的原型链
       Object.setPrototypeOf(arr, arrProto);
    }

    function observe(data){
        Object.keys(data).forEach(key => {
            let value = data[key];
            if (value != null && typeof value == 'object') {
                observe(value);
                observeArray(value)
            }
            definePropertyGet(data,key, value)
        })
    }


    // 劫持对象的getter setter
    function definePropertyGet(data, key, value){
        Object.defineProperty(data, key, {
            set:function(value){
                render();
            },
            get: function(){
                return value;
            }
        })
    }

    function render() {
        console.log('change!');
    }

    data.test = 2;
    data.arr.push(3);
    data.obj.p = 2;

    /**
     * 要求页面刷新的时候，上面3句语句执行，render方法能够打印出'change!'3次
     */
</script>
</html>
